#-------------------------------------------------------------------------------
# Name:        imgseq
# Purpose:     find and parse image sequences, and output list based on pattern
#
# Author:      Michael Auerswald
#
# Created:     23.02.2011
# Copyright:   (c) Michael Auerswald 2011
# Licence:     GPL
#-------------------------------------------------------------------------------
#!/usr/bin/env python

#import itertools
import re, os
import glob
import operator
import getopt
import sys

# Globals
imgFiles = []
groups = []

def isWin():
    if (os.name == "nt"):
        return True
    else:
        return False

class Config:
    path = os.curdir
    pattern = "nce"
    extension = ""
    numbered = False
    selectedGroup = -1
    ignoreMissing = False
    extList = ["gif","jpg","bmp","dpx","png","tif","tiff","psd","exr","jpeg"]

    def __init__(self):
        pass

class ImgFile:
    filename = ""
    extension = ""
    filepath = ""
    fullpath = ""
    counter = ""
    imgname = ""

    def __init__(self):
        pass

    def __init__(self, s):
        x = s.rfind('.',0); # last dot before extension
        y = s.rfind(os.sep,0); # last backslash
        splitpath = os.path.split(s)
        self.fileid = 0
        self.fullpath = os.path.normpath(s)
        self.extension = s[x+1:].lower()
        self.filename = s[y+1:x]
        self.filepath = splitpath[0]
        #self.filename = str(splitpath[1])
        #self.filepath = s[0:y]
        self.counter = re.findall(r"\d+$", self.filename)[0]
        self.imgname = self.filename[0:-len(self.counter)]

    def __iter__(self):
        return self

    def next(self):
        pass

# print results in a fasion that follows a pattern defined by the user
def printOutput(pattern):
    x = 0
    for i in groups:    # groups contains tuples of beginning and end indexes of sequences in imgFiles
        a = imgFiles[i[0]]  # first image of sequence
        b = imgFiles[i[1]]  # last image of sequence
        if (cfg.numbered):
            s = str(x)+": " # add a number that is also used for the -s parameter
        else:
            s = ""
        if (cfg.selectedGroup == -1):   # thats the default, if -s is not set, this just shows all
            for k in range(len(pattern)):
                if (pattern[k]=="n"):
                    s += a.imgname
                elif (pattern[k]=="c"):
                    if (i[0] != i[1]):
                        s += "["
                        s += a.counter + "-" + b.counter
                        s += "]"
                    else:
                        s += a.counter
                elif (pattern[k]=="C"):
                    s += "["
                    s += a.counter + "-" + b.counter
                    s += "]"
                elif (pattern[k]=="e"):
                    s += "." + a.extension
                elif (pattern[k]=="p"):
                    s += os.path.normpath(a.filepath) + os.sep
                elif (pattern[k]==","):
                    s += ","
                elif (pattern[k]==":"):
                    s += ":"
                elif (pattern[k]==" "):
                    s += " "
                elif (pattern[k]=="#"):
                    num = len(a.counter)
                    for whatever in range(1,num):
                        s += "#"
                elif (pattern[k]=="a"):
                    s += str(imgFiles[i[0]].counter)
                elif (pattern[k]=="b"):
                    s += str(imgFiles[i[1]].counter)
                elif (pattern[k]=="A"):
                    s += str(abs(int(imgFiles[i[0]].counter)))
                elif (pattern[k]=="B"):
                    s += str(abs(int(imgFiles[i[1]].counter)))
                else:
                    pass
            print os.path.normpath(s);
        elif (cfg.selectedGroup == x):
            for img in imgFiles[i[0]:i[1]+1]:
                s = os.path.normpath(img.fullpath)
                print s;
        elif (cfg.selectedGroup > len(groups)-1):
            print "Not a valid group number, valid range is 0 - " + str(len(groups)-1)
            sys.exit(2)
        else:
            pass
        x+=1

def usage():
    print "ImgSeq.py - find image sequences and show patterned output"
    print "Usage: python imgseq.py <arguments>"
    print "Arguments:"
    print "-e --extension <extension>: limit results to this extension"
    print "-h --help: this help"
    print "-i --ignoremissing: ignore missing images in a sequence"
    print "-n --numbered: give each sequence a number that can be used with -s"
    print "-p --path <path>: path to check for image sequences"
    print "-s --select: output a single (-n) sequence of images"
    print "-t --pattern \"pattern\": output pattern. Default is \"nce\"."
    print "\n"
    print "Patterns:"
    print "a: first image of sequence counter"
    print "A: same, but absolute number"
    print "b: last image of sequence counter"
    print "B: same, but absolute number"
    print "n: image name without counter"
    print "c: counter range (single images: image0001.jpg)"
    print "C: counter range (single images: image[0001-0001].jpg)"
    print "e: filter file extension(s) (comma separated)"
    print "p: image path"
    print "#: counter placeholder"
    print "allowed seperators: column(:) comma(,) space( )"

def getFileList(path):
    allfiles = []
    tmpfiles = []
    for i in cfg.extList:
        # glob() unfortunately does not support curly braces yet, so no *.{jpg,gif,png,bmp}
        tmpfiles = glob.glob(path + "*." + i);
        if (len(tmpfiles) > 0):
            allfiles += tmpfiles
    return allfiles


def main(argv):

    # Holds the command line parameters and other config data
    global cfg
    cfg = Config()

    # Read command line parameters using getopt
    try:
        opts, args = getopt.getopt(argv, "s:nhip:e:t:", ["select=", "numbered", "help", "ignoremissing", "path=", "extension=", "pattern="]);
    except getopt.GetoptError:
        usage();
        sys.exit(2);

    # Go through command line argument list
    for opt, arg in opts:
        if opt in ("-h", "--help"):
            usage()
            sys.exit()
        elif opt == '-d':
            global _debug
            _debug = 1
        elif opt in ("-p", "--path"):
            if (arg[-1] != os.sep):
                arg += os.sep
            cfg.path = arg
        elif opt in ("-n", "--numbered"):
            cfg.numbered = True
        elif opt in ("-s", "--select"):
            try:
                cfg.selectedGroup = int(arg)
            except:
                print "The -s/--select parameter needs a number as its argument."
                sys.exit(2)
        elif opt in ("-t", "--pattern"):
            cfg.pattern = arg
        elif opt in ("-i", "--ignoreMissing"):
            cfg.ignoreMissing = True
        elif opt in ("-e", "--extension"):
            cfg.extension = arg
            cfg.extList = cfg.extension.split(",")

    # escape backslashes in OS is Windows
    if (isWin()):
        cfg.path.replace("\\",os.sep+os.sep) #looks weird due to escaping...
    else:
        pass

    # only continue if path actually exists
    if (os.path.exists(os.path.normpath(cfg.path))):
        filelist_all = getFileList(cfg.path);
    else:
        print "Error: path does not exist \""+os.path.normpath(cfg.path)+"\"";
        sys.exit()


    # Take the resulting list of filenames (which are returned as full,
    # absolute paths) and make an ImgFile object out of each list entry.
    i = 0;
    for f in filelist_all:
        n = ImgFile(f);
        n.fileid=i;
        i+=1;
        imgFiles.append(n);

    # Sort the list of ImgFile objects by their extension first,
    # then the image name, then the counter this leaves us with
    # a sorted list that should be all images sequences in their
    # correct order.
    imgFiles.sort(key=lambda i:(i.extension,i.imgname,len(i.counter),i.counter));

    # Also add a fileid by just counting from 0 up - this will be used to
    # identify each result by a specific number
    i = 0;
    for n in imgFiles:
        n.fileid=i;
        i+=1;

    # These two are just temp variables
    grpstart = 0;
    grpend = 0;
    for i in imgFiles:
        #print i.filename, ":", i.imgname, ":", i.extension, ":", len(i.counter), i.counter;
        if (i.fileid < 1):
            prev = imgFiles[0];
            grpstart = i.fileid;
        else:
            prev = imgFiles[i.fileid-1];

        # If the image has the same name and extension as the previous one in the list, and the counter has the same number of digits, we can assume that it is part of a sequence
        # In this case we just skip to the next.
        # TODO: check that the counter is actually increased by one
        if (((i.extension == prev.extension) and (i.imgname == prev.imgname) and (len(i.counter) == len(prev.counter))) and (i.fileid < len(imgFiles)-1) ):
            if ((cfg.ignoreMissing or (int(i.counter) == (int(prev.counter)+1))) or (i.fileid == 0)):
                pass;
            else:
                grpend = prev.fileid;
                groups.append((grpstart,grpend));
                grpstart = i.fileid;
        # This is a special case for the very last entry in the list, which closes the image sequence it was in
        elif (((i.extension == prev.extension) and (i.imgname == prev.imgname) and (len(i.counter) == len(prev.counter))) and (i.fileid == len(imgFiles)-1) ):
            grpend = i.fileid;
            groups.append((grpstart,grpend));
        # If the image is not the same as the previous one (apart from the counter), we can assume that it's either a single image or a new sequence
        else:
            grpend = prev.fileid;
            groups.append((grpstart,grpend));
            grpstart = i.fileid;

    #print groups;
    printOutput(cfg.pattern);

if __name__ == '__main__':
    main(sys.argv[1:])